Introduction + Purpose
For this project, I wanted to create a linear regression model for different population demographics by US location and respective air quality in that location. This was very interesting to me because I am very interested in environmental discrimination such as environmental racism. Therefore, I wanted to know if factors such as poverty, race, and gender affect the air quality in respective areas because this could potentially point to a symptom of environmental discrimination.
Data Description + Sources
The data for poverty was in the form of percentage of people in poverty by US county. The racial data was organized by white, black, hispanic, asian, and native american percentages by US county. The gender data was male percentages by US county. The air quality data was organized by number of days data was recorded, the number of good, moderate, unhealthy, very unhealthy, and hazardous data.
Because data was only available for all counties for 2010 for all of the variables I wanted in my model, that is the data I chose to use, even though it is not the most recent.
The race and poverty by county data is from the US Census’ Data Mapper tool.
The poverty data is from the US Census’ Small Area Income and Poverty Estimates tool.
The air quality data is from the EPA yearly Air Quality Index Report (https://www.epa.gov/outdoor-air-quality-data/air-quality-index-report).
Data Reformatting/Parsing
The air quality data contains features for the number of days the air quality was recorded and subsequently, the number of good, moderate, unhealthy, and hazardous air quality days. The number of days the quality was recorded for all of the counties is not necessarily the same. Therefore, the features for number of good, moderate, etc. days was converted to a percentage of number of days recorded. This was done in R before database storage.
# load the air quality data into a data frame with unnecessary data
airQuality <- read.csv("/Users/vikaba/Documents/Documents/northeastern/second_year/DS4100/final_project/annual_aqi_by_county_2010.csv")[,c(-3,-7,-11:-19)]
# convert all good, moderate, etc. day counts to percentages of days data was recorded
for (i in 1:nrow(airQuality)) {
for (j in 4:8) {
airQuality[i,j] = (airQuality[i,j]/airQuality[i,3])*100
}
}
airQuality$State <- str_trim(as.character(airQuality$State))
airQuality$County <- str_trim(as.character(airQuality$County))
# export data back with the fixed air quality data
write.csv(airQuality, "AirQualityFixed.csv", row.names = FALSE)
The racial percentages were all in different tables. Before putting the data into the database, the race tables were combined into one table in Excel.
The racial, gender, and poverty tables all had “County” after the county names while the air quality data didn’t. In order to match up the data, “County” was deleted in those values. The poverty data also had state names as numbers and state initials in the county field. The initials were deleted and state numbers were converted to String names. This was all done in R before database storage
# load the race, gender, and poverty data into a data frame
race <- read.csv("/Users/vikaba/Documents/Documents/northeastern/second_year/DS4100/final_project/percent_race.csv")[-1915,] # row 1915 has spanish spelling
gender <- read.csv("/Users/vikaba/Documents/Documents/northeastern/second_year/DS4100/final_project/percent_male.csv")[-1915,] # row 1915 has spanish spelling
poverty <- read.csv("/Users/vikaba/Documents/Documents/northeastern/second_year/DS4100/final_project/percent_poverty.csv")
# delete all rows with states as Puerto Rico because the poverty data does not have Puerto Rico data
gender <- gender[gender$State != "Puerto Rico",]
race <- race[race$State != "Puerto Rico",]
# format race and gender data to not have "County" in the county name field
library(stringr)
gender$State <- as.character(gender$State)
gender$County <- as.character(gender$County)
race$State <- as.character(race$State)
race$County <- as.character(race$County)
for (i in 1:nrow(gender)) {
gender[i,2] <- gsub("County", "", gender[i,2])
gender[i,2] <- gsub("Borough", "", gender[i,2])
gender[i,2] <- str_trim(gsub("Census Area", "", gender[i,2]))
race[i,2] <- gsub("County", "", race[i,2])
race[i,2] <- gsub("Borough", "", race[i,2])
race[i,2] <- str_trim(gsub("Census Area", "", race[i,2]))
}
# format poverty data to not have state initials in county name and "County" and convert state numbers to state name
statesAbbs <- data.frame(state.abb, state.name)
statesAbbs$state.abb <- as.character(state.abb)
statesAbbs$state.name <- as.character(state.name)
poverty$State <- as.character(poverty$State)
poverty$County <- as.character(poverty$County)
for (i in 1:nrow(poverty)) {
state <- unlist(strsplit(poverty[i,2], "[()]"))[2]
poverty[i,1] <- statesAbbs[match(state, statesAbbs$state.abb),2]
poverty[i,2] <- gsub("County", "", poverty[i,2])
poverty[i,2] <- gsub("Census Area", "", poverty[i,2])
poverty[i,2] <- gsub("Borough", "", poverty[i,2])
getRidOfPars <- unlist(strsplit(poverty[i,2], "[(]"))[1]
poverty[i,2] <- str_trim(getRidOfPars)
}
# export data back with the fixed race, gender, and poverty data
write.csv(race, "RaceFixed.csv", row.names = FALSE)
write.csv(gender, "GenderFixed.csv", row.names = FALSE)
write.csv(poverty, "PovertyFixed.csv", row.names = FALSE)
Database Storage
A relational SQL database was then created in my mySQL to store all values for air quality measurements and the demographic measurements by state.
The database has tables for poverty, race, gender, citizenship, and air quality.
# install and load package to connect to mySQL
#install.packages("RMySQL")
library(RMySQL)
# establish a connection with the local envi_model database
driver <- dbDriver("MySQL")
conn <-
dbConnect(driver,
user = "root",
pass = "1234",
dbname = "envi_model")
There are multiple values for each state in the air quality data. Therefore, these values were aggregated by averaging in mySQL before joining all of the tables to create a master table.
In order to create a predictive model, the tables in the database are all then joined to create a master table of demographics and air quality by state.
# aggregate the air quality table by state and average all of the values for each of the columns
# join all of existing measuring tables by state
query <-
dbSendQuery(
conn,
statement = "SELECT DISTINCT
air_quality.state,
air_quality.county,
poverty.poverty_percent,
gender.male_percent,
race.white_percent,
race.black_percent,
race.hispanic_percent,
race.asian_percent,
race.native_percent,
air_quality.good_days,
air_quality.moderate_days,
air_quality.unhealthy_days,
air_quality.very_unhealthy_days,
air_quality.hazardous_days
FROM
(SELECT DISTINCT * FROM gender) AS gender,
(SELECT DISTINCT * FROM race) AS race,
(SELECT DISTINCT * FROM poverty) AS poverty,
(SELECT DISTINCT * FROM air_quality) AS air_quality
WHERE
gender.state = gender.state AND
gender.state = race.state
AND gender.county = race.county
AND race.state = poverty.state
AND race.county = poverty.county
AND poverty.state = air_quality.state
AND poverty.county = air_quality.county
ORDER BY air_quality.state"
)
allData <- fetch(query, n=-1)
write.csv(allData, "AllData.csv", row.names = FALSE)
Data Visualizations
Before creating a model for and evaluation the data, I used Tableau to create several visualizations that demonstrate some of the features by state and county as the data is interesting to visualize.
As can be seen from this visualization, the smaller circles which represent a low percentage of white people, are mostly darker colored than the bigger circles, meaning they have a higher poverty percentage.

Evaluating the Data
Outliers
First, outliers were identified in the response variable of good days by seeing which values were about 3 standard deviations from the mean.
Because this model deals with air quality data, I did not think it was necessary to omit these outliers. From looking at the outliers (of which there were only 8), the number of good days was low for those days because moderate or unhealthy days were high and I think that is important data to have. It also did not seem like these outliers were due to wrongful experimentation.
goodDaysMean <- mean(allData$good_days) # good days mean
goodDaysStDev <- sd(allData$good_days) # good days st dev
allData$stDevFromMean <- ((allData$good_days - goodDaysMean) / goodDaysStDev)
# find any values in good days that are 3 or more standard deviations from the mean
outliers <- allData[which(abs(allData$stDevFromMean) >= 3),]
outliers
Distribution
The response variable of good days was then analyzed for normal distribution with a histogram.
hist(allData$good_days)

A squared transform was then applied to the good days to normalize the good days variable.
# scatter plot of air quality good days data
hist((allData$good_days)^2)

# transform the average good days by square root
allDataGoodDaysNormalized <- allData[,c(-1,-2,-11:-15)]
allDataGoodDaysNormalized$good_days <- ((allData$good_days)^2)
ANOVA
ANOVA’s were done to see if certain features alone were enough to predict the percentage of good days for a location. These were done for male percentage, white percentage, and poverty percentage.
Then, ANOVA’s were done to see if these features with other features present were a significant predictor variable.
Male Percentage Only
# anova to see if male percentage alone is enough to predict number of good days
aov.genderOnly <- aov(formula = good_days ~ male_percent, data = allDataGoodDaysNormalized)
summary(aov.genderOnly)
Df Sum Sq Mean Sq F value Pr(>F)
male_percent 1 4.923e+08 492314114 88.19 <2e-16 ***
Residuals 1016 5.672e+09 5582325
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Because the p value is less than 0.05, male percentage alone is enough to predict number of good days.
White Percentage Only
# anova to see if white percentage alone is enough to predict number of good days
aov.whiteOnly <- aov(formula = good_days ~ white_percent, data = allDataGoodDaysNormalized)
summary(aov.whiteOnly)
Df Sum Sq Mean Sq F value Pr(>F)
white_percent 1 3.172e+08 317232571 55.13 2.39e-13 ***
Residuals 1016 5.847e+09 5754650
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Because the p value is less than 0.05, white percentage alone is enough to predict number of good days.
Poverty Percentage Only
# anova to see if poverty percentage alone is enough to predict number of good days
aov.povertyOnly <- aov(formula = good_days ~ poverty_percent, data = allDataGoodDaysNormalized)
summary(aov.povertyOnly)
Df Sum Sq Mean Sq F value Pr(>F)
poverty_percent 1 1.602e+07 16018420 2.647 0.104
Residuals 1016 6.148e+09 6051120
Because the p value is greater than 0.05, poverty percentage alone is not enough to predict number of good days.
All Percentages to See if Poverty, Male, and White Percentages Significant Predictors
# anova to see if poverty, white, and male percentages with other features are significant predictors
aov.goodDaysAll <- aov(formula = good_days ~ ., data = allDataGoodDaysNormalized)
summary(aov.goodDaysAll)
Df Sum Sq Mean Sq F value Pr(>F)
poverty_percent 1 1.602e+07 16018420 3.094 0.078907 .
male_percent 1 4.818e+08 481782726 93.043 < 2e-16 ***
white_percent 1 2.070e+08 206963851 39.970 3.87e-10 ***
black_percent 1 1.299e+08 129863906 25.080 6.49e-07 ***
hispanic_percent 1 1.383e+07 13826362 2.670 0.102555
asian_percent 1 5.903e+07 59025829 11.399 0.000763 ***
native_percent 1 2.665e+07 26653409 5.147 0.023492 *
Residuals 1010 5.230e+09 5178042
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
The p values for male and white percentages are less than 0.05, making them significant predictors. The p value for poverty percentage is greater than 0.05, therefore it is not a significant predictor when all of the other features present
Scatter Plots
Scatter plots were also done to see if there was a linear regression relationship between gender, race, and poverty features and air quality numbers
Male Percentage vs Good Days
scatter.smooth(allDataGoodDaysNormalized$male_percent, allDataGoodDaysNormalized$good_days)

There does not seem to be a strong correlation between good days and percent of males. Good days seem to be constant when compared to percentage of males.
White Percentage vs Good Days
scatter.smooth(allDataGoodDaysNormalized$white_percent, allDataGoodDaysNormalized$good_days)

In this plot, the data seems scattered without a significant correlation between white percentage and good days.
Male Percentage vs Good Days
scatter.smooth(allDataGoodDaysNormalized$poverty_percent, allDataGoodDaysNormalized$good_days)

This plot also shows no significant correlation as the points are very scattered.
Spearman Rank and Pearson Moment Coefficients
Spearman and Pearson coefficients were also used to assess correlation between poverty, race, and gender percentages
Before the tests were performed on these features, an effort was made to normalize them for better correlation coefficient testing. No transforms were made on any of the data. The poverty data was relatively normally distributed. The race and gender data was skewed but did not respond well to transforms.
Pearson Moment Coefficient: percent male and number of good air quality days
cor(allDataGoodDaysNormalized$good_days, allDataGoodDaysNormalized$male_percent, method = "pearson")
[1] 0.2826125
Spearman Rank Coefficient: percent male and number of good air quality days
cor(allDataGoodDaysNormalized$good_days, allDataGoodDaysNormalized$male_percent, method = "spearman")
[1] 0.3291413
Pearson Moment Coefficient: percent white and number of good air quality days
cor(allDataGoodDaysNormalized$good_days, allDataGoodDaysNormalized$white_percent, method = "pearson")
[1] 0.2268606
Spearman Rank Coefficient: percent white and number of good air quality days
cor(allDataGoodDaysNormalized$good_days, allDataGoodDaysNormalized$white_percent, method = "spearman")
[1] 0.24137
Pearson Moment Coefficient: percent poverty and number of good air quality days
cor(allDataGoodDaysNormalized$good_days, allDataGoodDaysNormalized$poverty_percent, method = "pearson")
[1] -0.05097768
Spearman Rank Coefficient: percent poverty and number of good air quality days
cor(allDataGoodDaysNormalized$good_days, allDataGoodDaysNormalized$poverty_percent, method = "spearman")
[1] -0.06818472
For all of the features evaluated, the Spearman Rank and Pearson Moment coefficients are relatively close to each other. This means that outliers do not seem to be having an effect on the data. However, all of the coefficients are very low and significantly below 0.8. Because none of the coefficients are 0, there seems to be some correlation between these features and the number of good air quality days, but not enough to say that there is significant correlation. There is the least correlation between percent poverty and good air quality and the most correlation between percent of males and good air quality. None of these correlations, however, are strong.
The race and gender data is also not normally distributed which may be affecting the correlation coefficients. However, because the Spearman Rank and Pearson Moment coefficients for these 2 features were relatively close, the distribution does not seem to be significantly affecting the correlation coefficients
Building the Model(s)
The desired model for this dataset is a multiple linear regression model with some response variable dealing with the number of days of a certain air quality.
Model with Good Days as Response Variable
The first model has the good days as a response variable.
The data was first randomly split up 50-50 into a training and validation sets.
# training data set: random half of the data
training <- allDataGoodDaysNormalized[sample(nrow(allDataGoodDaysNormalized), nrow(allDataGoodDaysNormalized) / 2),]
# test data set: the other half of the data
test <- allDataGoodDaysNormalized[-c(as.numeric(rownames(training))),]
To create a multiple linear regression model, backward fitting was used and features with p values > 0.05 were removed at each step.
# linear regression model using training data and backward fitting, only keeping p values < 0.05
#goodDaysModel <- lm(formula = good_days ~ ., data = training)
goodDaysModel <- lm(formula = good_days ~ .-(hispanic_percent+poverty_percent+asian_percent+black_percent), data = training)
summary(goodDaysModel)
Call:
lm(formula = good_days ~ . - (hispanic_percent + poverty_percent +
asian_percent + black_percent), data = training)
Residuals:
Min 1Q Median 3Q Max
-6714.4 -1757.0 39.4 1700.9 6561.8
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -11948.969 2911.884 -4.104 4.74e-05 ***
male_percent 281.376 59.463 4.732 2.89e-06 ***
white_percent 41.570 6.594 6.304 6.33e-10 ***
native_percent 70.824 19.852 3.568 0.000395 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2263 on 505 degrees of freedom
Multiple R-squared: 0.1387, Adjusted R-squared: 0.1336
F-statistic: 27.12 on 3 and 505 DF, p-value: 2.823e-16
Next, using the test data set was used to judge the accuracy of the model by calculating the prediction accuracy with the predicted values for the test data set. AIC and BIC calculations were also performed as they are good measures for model selection, in case other models are created for this data.
testUnnormalized <- test
testUnnormalized$good_days <- allData[-c(as.numeric(rownames(training))),c("good_days")]
goodDaysPreds <- predict(goodDaysModel, testUnnormalized)
# data frame of actual values vs predicted values
actuals_preds_good <- cbind(data.frame(actuals = testUnnormalized$good_days, predicteds = goodDaysPreds))
# prediction accuracy
correlation_accuracy <- cor(actuals_preds_good)
paste("Prediction Accuracy: ", correlation_accuracy[1,2] * 100, "%", sep="")
[1] "Prediction Accuracy: 38.6344403240153%"
# fit AIC and BIC calculations
paste("AIC:", AIC(goodDaysModel))
[1] "AIC: 9314.03660913136"
paste("BIC:", BIC(goodDaysModel))
[1] "BIC: 9335.19884921411"
Using the training and test data set to calculate the prediction accuracy, the model was correct about 39% of the time.
Model with Combo of Good and Moderate Days as Response Variable
To create this model, the sum of good and moderate days was taken to create the feature of acceptable air quality days.
allDataSumGoodModerate <- allData[,-15]
# create acceptable days feature
allDataSumGoodModerate$acceptable_days <- allData$good_days + allData$moderate_days
# only leave acceptable days feature in and omit out the rest of the air quality features
allDataSumGoodModerate <- allDataSumGoodModerate[,c(-1,-2,-10:-14)]
Then, outliers were identified again in the same method of standard deviation from the mean as the last model. Once again, I decided to leave these outliers in for more accurate data and because I believe this data is significant for analysis
acceptableDaysMean <- mean(allDataSumGoodModerate$acceptable_days) # acceptable days mean
acceptableDaysStDev <- sd(allDataSumGoodModerate$acceptable_days) # acceptable days st dev
# find any values in good days that are 3 or more standard deviations from the mean
allDataSumGoodModerate$stDevFromMean <- ((allDataSumGoodModerate$acceptable_days - acceptableDaysMean) / acceptableDaysStDev)
allDataSumGoodModerate[which(abs(allDataSumGoodModerate$stDevFromMean) >= 3),]
# omit the standard dev from mean columns for model creation
allDataSumGoodModerate <- allDataSumGoodModerate[,-9]
A histogram was then used to see the distribution of the response variable.
Because none of the transform had a significant effect on the distribution of the heavily right skewed data, the data was left as is.
hist(allDataSumGoodModerate$acceptable_days)

The data was then once again randomly split up 50-50 into training and test data sets.
# training data set: random half of the data
trainingAcceptableDays <- allDataSumGoodModerate[sample(nrow(allDataSumGoodModerate), nrow(allDataSumGoodModerate) / 2),]
# test data set: the other half of the data
testAcceptableDays <- allDataSumGoodModerate[-c(as.numeric(rownames(training))),]
Then, to create a multiple linear regression model, backward fitting was again used and features with p values > 0.05 were removed at each step.
# linear regression model using training data and backward fitting, only keeping p values < 0.05
acceptableDaysModel <- lm(formula = acceptable_days ~ .-(poverty_percent+male_percent), data = trainingAcceptableDays)
summary(acceptableDaysModel)
Call:
lm(formula = acceptable_days ~ . - (poverty_percent + male_percent),
data = trainingAcceptableDays)
Residuals:
Min 1Q Median 3Q Max
-72.472 -0.804 0.877 2.065 17.952
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 45.66604 6.96454 6.557 1.36e-10 ***
white_percent 0.53732 0.07117 7.550 2.06e-13 ***
black_percent 0.54617 0.07372 7.408 5.44e-13 ***
hispanic_percent 0.12156 0.02705 4.493 8.70e-06 ***
asian_percent 0.55904 0.11933 4.685 3.61e-06 ***
native_percent 0.60591 0.08652 7.003 8.04e-12 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 5.636 on 503 degrees of freedom
Multiple R-squared: 0.1406, Adjusted R-squared: 0.1321
F-statistic: 16.46 on 5 and 503 DF, p-value: 4.642e-15
Next, using the test data set was used to judge the accuracy of the model by calculating the prediction accuracy with the predicted values for the test data set. AIC and BIC calculations were also performed as they are good measures for model selection, in case other models are created for this data.
acceptableDaysPreds <- predict(acceptableDaysModel, testAcceptableDays)
# data frame of actual values vs predicted values
actuals_preds_acceptable <- cbind(data.frame(actuals = testAcceptableDays$acceptable_days, predicteds = acceptableDaysPreds))
# prediction accuracy
correlation_accuracy <- cor(actuals_preds_acceptable)
paste("Prediction Accuracy: ", correlation_accuracy[1,2] * 100, "%", sep="")
[1] "Prediction Accuracy: 24.4240395426979%"
# fit AIC and BIC calculations
paste("AIC:", AIC(acceptableDaysModel))
[1] "AIC: 3212.77970365574"
paste("BIC:", BIC(acceptableDaysModel))
[1] "BIC: 3242.40683977159"
Using the training and test data set to calculate the prediction accuracy, the model was correct about 24% of the time.
Evalulation of Models
Model 1: Good Days as Response Variable
Overall, this model is not a very good measure for predicting the percentage of good air quality days a location will have. Both the multiple R squared and the adjusted R squared are very low and are significantly below 0.7. This means that the variation of good air quality days is not significantly explained by this model. The calculated MAD for this model is 28.66 which is not very close to 0, meaning there is a significant amount of deviation in this model. The model is statistically significant because the p-values for each feature as well as the overall model p-value are well below 0.05.
Model 2: Good+Moderate Days as Response Variable
This model is also not a great measure for predicting the percentage of acceptable days (that is, good and moderate days). The multiple R squared and the adjusted R sqaures are even lower in this model than the previous model. This means that even less variation in the acceptable air quality days is described by this model than the previous model. The calculated MAD for this model, however, is 0.074, which is much closer to 0 than the previous model. Meaning that there is much less deviation in this model than the previous model. This makes sense because outside of the 15 outliers, the percentage of acceptable days was mostly around 90%. This model is also statistically significant with overall and individual feature p-values well below 0.05. The AIC and BIC of this model are also much lower than the previous model, meaning this model is more likely to be the true model and is closer to the truth.
Conclusion
Overall, I hypothesized that building a model with these above demographic features would be a good predictor for air quality is US locations. However, from the two models I have built and the data I aggregated, this does not seem to be the case.
There are a few reasons why this was the result:
-
There is not enough air quality data. While there were around 3,000 records from the census demographic data, there were only about 1,000 records for the air quality data. This means that areas that could have contributed to a more successful model did not have air quality recorded.
-
The correlation actually does not exist. I do not believe this is the case, because it has been shown that the correlation exists for water quality and the many environmental racism cases in the US.
-
The 2010 data is too old and in 2010, air quality was not dependent on any demographics. I don’t, however, think this was the case as 2010 was only 7 years ago.
LS0tCnRpdGxlOiBEZW1vZ3JhcGhpY3MgYW5kIEFpciBRdWFsaXR5IGJ5IFVTIENvdW50aWVzCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCjxoMj4gSW50cm9kdWN0aW9uICsgUHVycG9zZSA8L2gyPgo8cD4gRm9yIHRoaXMgcHJvamVjdCwgSSB3YW50ZWQgdG8gY3JlYXRlIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgZm9yIGRpZmZlcmVudCBwb3B1bGF0aW9uIGRlbW9ncmFwaGljcyBieSBVUyBsb2NhdGlvbiBhbmQgcmVzcGVjdGl2ZSBhaXIgcXVhbGl0eSBpbiB0aGF0IGxvY2F0aW9uLgpUaGlzIHdhcyB2ZXJ5IGludGVyZXN0aW5nIHRvIG1lIGJlY2F1c2UgSSBhbSB2ZXJ5IGludGVyZXN0ZWQgaW4gZW52aXJvbm1lbnRhbCBkaXNjcmltaW5hdGlvbiBzdWNoIGFzIGVudmlyb25tZW50YWwgcmFjaXNtLiBUaGVyZWZvcmUsIEkgd2FudGVkIHRvIGtub3cgaWYgZmFjdG9ycyBzdWNoIGFzIHBvdmVydHksIHJhY2UsIGFuZCBnZW5kZXIgYWZmZWN0IHRoZSBhaXIgcXVhbGl0eSBpbiByZXNwZWN0aXZlIGFyZWFzIGJlY2F1c2UgdGhpcyBjb3VsZCBwb3RlbnRpYWxseSBwb2ludCB0byBhIHN5bXB0b20gb2YgZW52aXJvbm1lbnRhbCBkaXNjcmltaW5hdGlvbi4gPC9wPgoKPGgyPiBEYXRhIERlc2NyaXB0aW9uICsgU291cmNlcyA8L2gyPgo8cD4gVGhlIGRhdGEgZm9yIHBvdmVydHkgd2FzIGluIHRoZSBmb3JtIG9mIHBlcmNlbnRhZ2Ugb2YgcGVvcGxlIGluIHBvdmVydHkgYnkgVVMgY291bnR5LiBUaGUgcmFjaWFsIGRhdGEgd2FzIG9yZ2FuaXplZCBieSB3aGl0ZSwgYmxhY2ssIGhpc3BhbmljLCBhc2lhbiwgYW5kIG5hdGl2ZSBhbWVyaWNhbiBwZXJjZW50YWdlcyBieSBVUyBjb3VudHkuIFRoZSBnZW5kZXIgZGF0YSB3YXMgbWFsZSBwZXJjZW50YWdlcyBieSBVUyBjb3VudHkuIFRoZSBhaXIgcXVhbGl0eSBkYXRhIHdhcyBvcmdhbml6ZWQgYnkgbnVtYmVyIG9mIGRheXMgZGF0YSB3YXMgcmVjb3JkZWQsIHRoZSBudW1iZXIgb2YgZ29vZCwgbW9kZXJhdGUsIHVuaGVhbHRoeSwgdmVyeSB1bmhlYWx0aHksIGFuZCBoYXphcmRvdXMgZGF0YS48L3A+CjxwPiBCZWNhdXNlIGRhdGEgd2FzIG9ubHkgYXZhaWxhYmxlIGZvciBhbGwgY291bnRpZXMgZm9yIDIwMTAgZm9yIGFsbCBvZiB0aGUgdmFyaWFibGVzIEkgd2FudGVkIGluIG15IG1vZGVsLCB0aGF0IGlzIHRoZSBkYXRhIEkgY2hvc2UgdG8gdXNlLCBldmVuIHRob3VnaCBpdCBpcyBub3QgdGhlIG1vc3QgcmVjZW50LiA8L3A+CjxwPiBUaGUgcmFjZSBhbmQgcG92ZXJ0eSBieSBjb3VudHkgZGF0YSBpcyBmcm9tIHRoZSBVUyBDZW5zdXMnIERhdGEgTWFwcGVyIHRvb2wuPC9wPgo8cD4gVGhlIHBvdmVydHkgZGF0YSBpcyBmcm9tIHRoZSBVUyBDZW5zdXMnIFNtYWxsIEFyZWEgSW5jb21lIGFuZCBQb3ZlcnR5IEVzdGltYXRlcyB0b29sLjwvcD4KPHA+IFRoZSBhaXIgcXVhbGl0eSBkYXRhIGlzIGZyb20gdGhlIEVQQSB5ZWFybHkgQWlyIFF1YWxpdHkgSW5kZXggUmVwb3J0IChodHRwczovL3d3dy5lcGEuZ292L291dGRvb3ItYWlyLXF1YWxpdHktZGF0YS9haXItcXVhbGl0eS1pbmRleC1yZXBvcnQpLjwvcD4KCjxoMj4gRGF0YSBSZWZvcm1hdHRpbmcvUGFyc2luZyA8L2gyPgpUaGUgYWlyIHF1YWxpdHkgZGF0YSBjb250YWlucyBmZWF0dXJlcyBmb3IgdGhlIG51bWJlciBvZiBkYXlzIHRoZSBhaXIgcXVhbGl0eSB3YXMgcmVjb3JkZWQgYW5kIHN1YnNlcXVlbnRseSwgdGhlIG51bWJlciBvZiBnb29kLCBtb2RlcmF0ZSwgdW5oZWFsdGh5LCBhbmQgaGF6YXJkb3VzIGFpciBxdWFsaXR5IGRheXMuIFRoZSBudW1iZXIgb2YgZGF5cyB0aGUgcXVhbGl0eSB3YXMgcmVjb3JkZWQgZm9yIGFsbCBvZiB0aGUgY291bnRpZXMgaXMgbm90IG5lY2Vzc2FyaWx5IHRoZSBzYW1lLiBUaGVyZWZvcmUsIHRoZSBmZWF0dXJlcyBmb3IgbnVtYmVyIG9mIGdvb2QsIG1vZGVyYXRlLCBldGMuIGRheXMgd2FzIGNvbnZlcnRlZCB0byBhIHBlcmNlbnRhZ2Ugb2YgbnVtYmVyIG9mIGRheXMgcmVjb3JkZWQuIFRoaXMgd2FzIGRvbmUgaW4gUiBiZWZvcmUgZGF0YWJhc2Ugc3RvcmFnZS4gPC9wPgpgYGB7cn0KIyBsb2FkIHRoZSBhaXIgcXVhbGl0eSBkYXRhIGludG8gYSBkYXRhIGZyYW1lIHdpdGggdW5uZWNlc3NhcnkgZGF0YQphaXJRdWFsaXR5IDwtIHJlYWQuY3N2KCIvVXNlcnMvdmlrYWJhL0RvY3VtZW50cy9Eb2N1bWVudHMvbm9ydGhlYXN0ZXJuL3NlY29uZF95ZWFyL0RTNDEwMC9maW5hbF9wcm9qZWN0L2FubnVhbF9hcWlfYnlfY291bnR5XzIwMTAuY3N2IilbLGMoLTMsLTcsLTExOi0xOSldCgojIGNvbnZlcnQgYWxsIGdvb2QsIG1vZGVyYXRlLCBldGMuIGRheSBjb3VudHMgdG8gcGVyY2VudGFnZXMgb2YgZGF5cyBkYXRhIHdhcyByZWNvcmRlZApmb3IgKGkgaW4gMTpucm93KGFpclF1YWxpdHkpKSB7CiAgZm9yIChqIGluIDQ6OCkgewogICAgYWlyUXVhbGl0eVtpLGpdID0gKGFpclF1YWxpdHlbaSxqXS9haXJRdWFsaXR5W2ksM10pKjEwMAogIH0KfQphaXJRdWFsaXR5JFN0YXRlIDwtIHN0cl90cmltKGFzLmNoYXJhY3RlcihhaXJRdWFsaXR5JFN0YXRlKSkKYWlyUXVhbGl0eSRDb3VudHkgPC0gc3RyX3RyaW0oYXMuY2hhcmFjdGVyKGFpclF1YWxpdHkkQ291bnR5KSkKCiMgZXhwb3J0IGRhdGEgYmFjayB3aXRoIHRoZSBmaXhlZCBhaXIgcXVhbGl0eSBkYXRhCndyaXRlLmNzdihhaXJRdWFsaXR5LCAiQWlyUXVhbGl0eUZpeGVkLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKPHA+IFRoZSByYWNpYWwgcGVyY2VudGFnZXMgd2VyZSBhbGwgaW4gZGlmZmVyZW50IHRhYmxlcy4gQmVmb3JlIHB1dHRpbmcgdGhlIGRhdGEgaW50byB0aGUgZGF0YWJhc2UsIHRoZSByYWNlIHRhYmxlcyB3ZXJlIGNvbWJpbmVkIGludG8gb25lIHRhYmxlIGluIEV4Y2VsLiA8L3A+CjxwPiBUaGUgcmFjaWFsLCBnZW5kZXIsIGFuZCBwb3ZlcnR5IHRhYmxlcyBhbGwgaGFkICJDb3VudHkiIGFmdGVyIHRoZSBjb3VudHkgbmFtZXMgd2hpbGUgdGhlIGFpciBxdWFsaXR5IGRhdGEgZGlkbid0LiBJbiBvcmRlciB0byBtYXRjaCB1cCB0aGUgZGF0YSwgIkNvdW50eSIgd2FzIGRlbGV0ZWQgaW4gdGhvc2UgdmFsdWVzLiBUaGUgcG92ZXJ0eSBkYXRhIGFsc28gaGFkIHN0YXRlIG5hbWVzIGFzIG51bWJlcnMgYW5kIHN0YXRlIGluaXRpYWxzIGluIHRoZSBjb3VudHkgZmllbGQuIFRoZSBpbml0aWFscyB3ZXJlIGRlbGV0ZWQgYW5kIHN0YXRlIG51bWJlcnMgd2VyZSBjb252ZXJ0ZWQgdG8gU3RyaW5nIG5hbWVzLiBUaGlzIHdhcyBhbGwgZG9uZSBpbiBSIGJlZm9yZSBkYXRhYmFzZSBzdG9yYWdlIDwvcD4KYGBge3J9CiMgbG9hZCB0aGUgcmFjZSwgZ2VuZGVyLCBhbmQgcG92ZXJ0eSBkYXRhIGludG8gYSBkYXRhIGZyYW1lCnJhY2UgPC0gcmVhZC5jc3YoIi9Vc2Vycy92aWthYmEvRG9jdW1lbnRzL0RvY3VtZW50cy9ub3J0aGVhc3Rlcm4vc2Vjb25kX3llYXIvRFM0MTAwL2ZpbmFsX3Byb2plY3QvcGVyY2VudF9yYWNlLmNzdiIpWy0xOTE1LF0gIyByb3cgMTkxNSBoYXMgc3BhbmlzaCBzcGVsbGluZwpnZW5kZXIgPC0gcmVhZC5jc3YoIi9Vc2Vycy92aWthYmEvRG9jdW1lbnRzL0RvY3VtZW50cy9ub3J0aGVhc3Rlcm4vc2Vjb25kX3llYXIvRFM0MTAwL2ZpbmFsX3Byb2plY3QvcGVyY2VudF9tYWxlLmNzdiIpWy0xOTE1LF0gIyByb3cgMTkxNSBoYXMgc3BhbmlzaCBzcGVsbGluZwpwb3ZlcnR5IDwtIHJlYWQuY3N2KCIvVXNlcnMvdmlrYWJhL0RvY3VtZW50cy9Eb2N1bWVudHMvbm9ydGhlYXN0ZXJuL3NlY29uZF95ZWFyL0RTNDEwMC9maW5hbF9wcm9qZWN0L3BlcmNlbnRfcG92ZXJ0eS5jc3YiKQoKIyBkZWxldGUgYWxsIHJvd3Mgd2l0aCBzdGF0ZXMgYXMgUHVlcnRvIFJpY28gYmVjYXVzZSB0aGUgcG92ZXJ0eSBkYXRhIGRvZXMgbm90IGhhdmUgUHVlcnRvIFJpY28gZGF0YQpnZW5kZXIgPC0gZ2VuZGVyW2dlbmRlciRTdGF0ZSAhPSAiUHVlcnRvIFJpY28iLF0KcmFjZSA8LSByYWNlW3JhY2UkU3RhdGUgIT0gIlB1ZXJ0byBSaWNvIixdCgojIGZvcm1hdCByYWNlIGFuZCBnZW5kZXIgZGF0YSB0byBub3QgaGF2ZSAiQ291bnR5IiBpbiB0aGUgY291bnR5IG5hbWUgZmllbGQKbGlicmFyeShzdHJpbmdyKQpnZW5kZXIkU3RhdGUgPC0gYXMuY2hhcmFjdGVyKGdlbmRlciRTdGF0ZSkKZ2VuZGVyJENvdW50eSA8LSBhcy5jaGFyYWN0ZXIoZ2VuZGVyJENvdW50eSkKcmFjZSRTdGF0ZSA8LSBhcy5jaGFyYWN0ZXIocmFjZSRTdGF0ZSkKcmFjZSRDb3VudHkgPC0gYXMuY2hhcmFjdGVyKHJhY2UkQ291bnR5KQpmb3IgKGkgaW4gMTpucm93KGdlbmRlcikpIHsKICBnZW5kZXJbaSwyXSA8LSBnc3ViKCJDb3VudHkiLCAiIiwgZ2VuZGVyW2ksMl0pCiAgZ2VuZGVyW2ksMl0gPC0gZ3N1YigiQm9yb3VnaCIsICIiLCBnZW5kZXJbaSwyXSkKICBnZW5kZXJbaSwyXSA8LSBzdHJfdHJpbShnc3ViKCJDZW5zdXMgQXJlYSIsICIiLCBnZW5kZXJbaSwyXSkpCiAgcmFjZVtpLDJdIDwtIGdzdWIoIkNvdW50eSIsICIiLCByYWNlW2ksMl0pCiAgcmFjZVtpLDJdIDwtIGdzdWIoIkJvcm91Z2giLCAiIiwgcmFjZVtpLDJdKQogIHJhY2VbaSwyXSA8LSBzdHJfdHJpbShnc3ViKCJDZW5zdXMgQXJlYSIsICIiLCByYWNlW2ksMl0pKQp9CgojIGZvcm1hdCBwb3ZlcnR5IGRhdGEgdG8gbm90IGhhdmUgc3RhdGUgaW5pdGlhbHMgaW4gY291bnR5IG5hbWUgYW5kICJDb3VudHkiIGFuZCBjb252ZXJ0IHN0YXRlIG51bWJlcnMgdG8gc3RhdGUgbmFtZQpzdGF0ZXNBYmJzIDwtIGRhdGEuZnJhbWUoc3RhdGUuYWJiLCBzdGF0ZS5uYW1lKQpzdGF0ZXNBYmJzJHN0YXRlLmFiYiA8LSBhcy5jaGFyYWN0ZXIoc3RhdGUuYWJiKQpzdGF0ZXNBYmJzJHN0YXRlLm5hbWUgPC0gYXMuY2hhcmFjdGVyKHN0YXRlLm5hbWUpCnBvdmVydHkkU3RhdGUgPC0gYXMuY2hhcmFjdGVyKHBvdmVydHkkU3RhdGUpCnBvdmVydHkkQ291bnR5IDwtIGFzLmNoYXJhY3Rlcihwb3ZlcnR5JENvdW50eSkKZm9yIChpIGluIDE6bnJvdyhwb3ZlcnR5KSkgewogIHN0YXRlIDwtIHVubGlzdChzdHJzcGxpdChwb3ZlcnR5W2ksMl0sICJbKCldIikpWzJdCiAgcG92ZXJ0eVtpLDFdIDwtIHN0YXRlc0FiYnNbbWF0Y2goc3RhdGUsIHN0YXRlc0FiYnMkc3RhdGUuYWJiKSwyXQogIHBvdmVydHlbaSwyXSA8LSBnc3ViKCJDb3VudHkiLCAiIiwgcG92ZXJ0eVtpLDJdKQogIHBvdmVydHlbaSwyXSA8LSBnc3ViKCJDZW5zdXMgQXJlYSIsICIiLCBwb3ZlcnR5W2ksMl0pCiAgcG92ZXJ0eVtpLDJdIDwtIGdzdWIoIkJvcm91Z2giLCAiIiwgcG92ZXJ0eVtpLDJdKQogIGdldFJpZE9mUGFycyA8LSB1bmxpc3Qoc3Ryc3BsaXQocG92ZXJ0eVtpLDJdLCAiWyhdIikpWzFdCiAgcG92ZXJ0eVtpLDJdIDwtIHN0cl90cmltKGdldFJpZE9mUGFycykKfQoKIyBleHBvcnQgZGF0YSBiYWNrIHdpdGggdGhlIGZpeGVkIHJhY2UsIGdlbmRlciwgYW5kIHBvdmVydHkgZGF0YQp3cml0ZS5jc3YocmFjZSwgIlJhY2VGaXhlZC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KGdlbmRlciwgIkdlbmRlckZpeGVkLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQp3cml0ZS5jc3YocG92ZXJ0eSwgIlBvdmVydHlGaXhlZC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCjxoMj4gRGF0YWJhc2UgU3RvcmFnZSA8L2gyPgo8cD5BIHJlbGF0aW9uYWwgU1FMIGRhdGFiYXNlIHdhcyB0aGVuIGNyZWF0ZWQgaW4gbXkgbXlTUUwgdG8gc3RvcmUgYWxsIHZhbHVlcyBmb3IgYWlyIHF1YWxpdHkgbWVhc3VyZW1lbnRzIGFuZCB0aGUgZGVtb2dyYXBoaWMgbWVhc3VyZW1lbnRzIGJ5IHN0YXRlLjwvcD4KPHA+VGhlIGRhdGFiYXNlIGhhcyB0YWJsZXMgZm9yIHBvdmVydHksIHJhY2UsIGdlbmRlciwgY2l0aXplbnNoaXAsIGFuZCBhaXIgcXVhbGl0eS48L3A+CmBgYHtyfQojIGluc3RhbGwgYW5kIGxvYWQgcGFja2FnZSB0byBjb25uZWN0IHRvIG15U1FMCiNpbnN0YWxsLnBhY2thZ2VzKCJSTXlTUUwiKQpsaWJyYXJ5KFJNeVNRTCkKCiMgZXN0YWJsaXNoIGEgY29ubmVjdGlvbiB3aXRoIHRoZSBsb2NhbCBlbnZpX21vZGVsIGRhdGFiYXNlCmRyaXZlciA8LSBkYkRyaXZlcigiTXlTUUwiKQpjb25uIDwtCiAgZGJDb25uZWN0KGRyaXZlciwKICAgICAgICAgICAgdXNlciA9ICJyb290IiwKICAgICAgICAgICAgcGFzcyA9ICIxMjM0IiwKICAgICAgICAgICAgZGJuYW1lID0gImVudmlfbW9kZWwiKQpgYGAKPHA+VGhlcmUgYXJlIG11bHRpcGxlIHZhbHVlcyBmb3IgZWFjaCBzdGF0ZSBpbiB0aGUgYWlyIHF1YWxpdHkgZGF0YS4gVGhlcmVmb3JlLCB0aGVzZSB2YWx1ZXMgd2VyZSBhZ2dyZWdhdGVkIGJ5IGF2ZXJhZ2luZyBpbiBteVNRTCBiZWZvcmUgam9pbmluZyBhbGwgb2YgdGhlIHRhYmxlcyB0byBjcmVhdGUgYSBtYXN0ZXIgdGFibGUuPC9wPgo8cD5JbiBvcmRlciB0byBjcmVhdGUgYSBwcmVkaWN0aXZlIG1vZGVsLCB0aGUgdGFibGVzIGluIHRoZSBkYXRhYmFzZSBhcmUgYWxsIHRoZW4gam9pbmVkIHRvIGNyZWF0ZSBhIG1hc3RlciB0YWJsZSBvZiBkZW1vZ3JhcGhpY3MgYW5kIGFpciBxdWFsaXR5IGJ5IHN0YXRlLiA8L3A+CmBgYHtyfQojIGFnZ3JlZ2F0ZSB0aGUgYWlyIHF1YWxpdHkgdGFibGUgYnkgc3RhdGUgYW5kIGF2ZXJhZ2UgYWxsIG9mIHRoZSB2YWx1ZXMgZm9yIGVhY2ggb2YgdGhlIGNvbHVtbnMKIyBqb2luIGFsbCBvZiBleGlzdGluZyBtZWFzdXJpbmcgdGFibGVzIGJ5IHN0YXRlCnF1ZXJ5IDwtCiAgZGJTZW5kUXVlcnkoCiAgICBjb25uLAogICAgc3RhdGVtZW50ID0gIlNFTEVDVCBESVNUSU5DVAoJYWlyX3F1YWxpdHkuc3RhdGUsCiAgICBhaXJfcXVhbGl0eS5jb3VudHksCiAgICBwb3ZlcnR5LnBvdmVydHlfcGVyY2VudCwKICAgIGdlbmRlci5tYWxlX3BlcmNlbnQsCiAgICByYWNlLndoaXRlX3BlcmNlbnQsCiAgICByYWNlLmJsYWNrX3BlcmNlbnQsCiAgICByYWNlLmhpc3BhbmljX3BlcmNlbnQsCiAgICByYWNlLmFzaWFuX3BlcmNlbnQsCiAgICByYWNlLm5hdGl2ZV9wZXJjZW50LAogICAgYWlyX3F1YWxpdHkuZ29vZF9kYXlzLAogICAgYWlyX3F1YWxpdHkubW9kZXJhdGVfZGF5cywKICAgIGFpcl9xdWFsaXR5LnVuaGVhbHRoeV9kYXlzLAogICAgYWlyX3F1YWxpdHkudmVyeV91bmhlYWx0aHlfZGF5cywKICAgIGFpcl9xdWFsaXR5LmhhemFyZG91c19kYXlzCkZST00KICAgIChTRUxFQ1QgRElTVElOQ1QgKiBGUk9NIGdlbmRlcikgQVMgZ2VuZGVyLAogICAgKFNFTEVDVCBESVNUSU5DVCAqIEZST00gcmFjZSkgQVMgcmFjZSwKICAgIChTRUxFQ1QgRElTVElOQ1QgKiBGUk9NIHBvdmVydHkpIEFTIHBvdmVydHksCiAgICAoU0VMRUNUIERJU1RJTkNUICogRlJPTSBhaXJfcXVhbGl0eSkgQVMgYWlyX3F1YWxpdHkKV0hFUkUKCWdlbmRlci5zdGF0ZSA9IGdlbmRlci5zdGF0ZSBBTkQKICAgIGdlbmRlci5zdGF0ZSA9IHJhY2Uuc3RhdGUKICAgICAgICBBTkQgZ2VuZGVyLmNvdW50eSA9IHJhY2UuY291bnR5CiAgICAgICAgQU5EIHJhY2Uuc3RhdGUgPSBwb3ZlcnR5LnN0YXRlCgkJQU5EIHJhY2UuY291bnR5ID0gcG92ZXJ0eS5jb3VudHkKICAgICAgICBBTkQgcG92ZXJ0eS5zdGF0ZSA9IGFpcl9xdWFsaXR5LnN0YXRlCiAgICAgICAgQU5EIHBvdmVydHkuY291bnR5ID0gYWlyX3F1YWxpdHkuY291bnR5Ck9SREVSIEJZIGFpcl9xdWFsaXR5LnN0YXRlIgogICkKYWxsRGF0YSA8LSBmZXRjaChxdWVyeSwgbj0tMSkKd3JpdGUuY3N2KGFsbERhdGEsICJBbGxEYXRhLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKPGgyPiBEYXRhIFZpc3VhbGl6YXRpb25zIDwvaDI+CjxwPkJlZm9yZSBjcmVhdGluZyBhIG1vZGVsIGZvciBhbmQgZXZhbHVhdGlvbiB0aGUgZGF0YSwgSSB1c2VkIFRhYmxlYXUgdG8gY3JlYXRlIHNldmVyYWwgdmlzdWFsaXphdGlvbnMgdGhhdCBkZW1vbnN0cmF0ZSBzb21lIG9mIHRoZSBmZWF0dXJlcyBieSBzdGF0ZSBhbmQgY291bnR5IGFzIHRoZSBkYXRhIGlzIGludGVyZXN0aW5nIHRvIHZpc3VhbGl6ZS48L3A+Cgo8aW1nIHNyYyA9ICIvVXNlcnMvdmlrYWJhL0RvY3VtZW50cy9Eb2N1bWVudHMvbm9ydGhlYXN0ZXJuL3NlY29uZF95ZWFyL0RTNDEwMC9maW5hbF9wcm9qZWN0L1BvdmVydHkgQnkgU3RhdGUucG5nIj4KPGltZyBzcmMgPSAiL1VzZXJzL3Zpa2FiYS9Eb2N1bWVudHMvRG9jdW1lbnRzL25vcnRoZWFzdGVybi9zZWNvbmRfeWVhci9EUzQxMDAvZmluYWxfcHJvamVjdC9XaGl0ZSBCeSBTdGF0ZS5wbmciPgo8aW1nIHNyYyA9ICIvVXNlcnMvdmlrYWJhL0RvY3VtZW50cy9Eb2N1bWVudHMvbm9ydGhlYXN0ZXJuL3NlY29uZF95ZWFyL0RTNDEwMC9maW5hbF9wcm9qZWN0L1BvdmVydHkgdnMgUGVyY2VudCBvZiBXaGl0ZSBQZW9wbGUgcGVyIENvdW50eS5wbmciPgo8cD4gQXMgY2FuIGJlIHNlZW4gZnJvbSB0aGlzIHZpc3VhbGl6YXRpb24sIHRoZSBzbWFsbGVyIGNpcmNsZXMgd2hpY2ggcmVwcmVzZW50IGEgbG93IHBlcmNlbnRhZ2Ugb2Ygd2hpdGUgcGVvcGxlLCBhcmUgbW9zdGx5IGRhcmtlciBjb2xvcmVkIHRoYW4gdGhlIGJpZ2dlciBjaXJjbGVzLCBtZWFuaW5nIHRoZXkgaGF2ZSBhIGhpZ2hlciBwb3ZlcnR5IHBlcmNlbnRhZ2UuIDwvcD4KPGltZyBzcmMgPSAiL1VzZXJzL3Zpa2FiYS9Eb2N1bWVudHMvRG9jdW1lbnRzL25vcnRoZWFzdGVybi9zZWNvbmRfeWVhci9EUzQxMDAvZmluYWxfcHJvamVjdC9Hb29kIERheXMgQnkgU3RhdGUucG5nIj4KCjxoMj4gRXZhbHVhdGluZyB0aGUgRGF0YSA8L2gyPgoKPGg0PiBPdXRsaWVycyA8L2g0Pgo8cD5GaXJzdCwgb3V0bGllcnMgd2VyZSBpZGVudGlmaWVkIGluIHRoZSByZXNwb25zZSB2YXJpYWJsZSBvZiBnb29kIGRheXMgYnkgc2VlaW5nIHdoaWNoIHZhbHVlcyB3ZXJlIGFib3V0IDMgc3RhbmRhcmQgZGV2aWF0aW9ucyBmcm9tIHRoZSBtZWFuLiA8L3A+CjxwPiBCZWNhdXNlIHRoaXMgbW9kZWwgZGVhbHMgd2l0aCBhaXIgcXVhbGl0eSBkYXRhLCBJIGRpZCBub3QgdGhpbmsgaXQgd2FzIG5lY2Vzc2FyeSB0byBvbWl0IHRoZXNlIG91dGxpZXJzLiBGcm9tIGxvb2tpbmcgYXQgdGhlIG91dGxpZXJzIChvZiB3aGljaCB0aGVyZSB3ZXJlIG9ubHkgOCksIHRoZSBudW1iZXIgb2YgZ29vZCBkYXlzIHdhcyBsb3cgZm9yIHRob3NlIGRheXMgYmVjYXVzZSBtb2RlcmF0ZSBvciB1bmhlYWx0aHkgZGF5cyB3ZXJlIGhpZ2ggYW5kIEkgdGhpbmsgdGhhdCBpcyBpbXBvcnRhbnQgZGF0YSB0byBoYXZlLiBJdCBhbHNvIGRpZCBub3Qgc2VlbSBsaWtlIHRoZXNlIG91dGxpZXJzIHdlcmUgZHVlIHRvIHdyb25nZnVsIGV4cGVyaW1lbnRhdGlvbi4gPC9wPgpgYGB7cn0KZ29vZERheXNNZWFuIDwtIG1lYW4oYWxsRGF0YSRnb29kX2RheXMpICMgZ29vZCBkYXlzIG1lYW4KZ29vZERheXNTdERldiA8LSBzZChhbGxEYXRhJGdvb2RfZGF5cykgIyBnb29kIGRheXMgc3QgZGV2CmFsbERhdGEkc3REZXZGcm9tTWVhbiA8LSAoKGFsbERhdGEkZ29vZF9kYXlzIC0gZ29vZERheXNNZWFuKSAvIGdvb2REYXlzU3REZXYpCgojIGZpbmQgYW55IHZhbHVlcyBpbiBnb29kIGRheXMgdGhhdCBhcmUgMyBvciBtb3JlIHN0YW5kYXJkIGRldmlhdGlvbnMgZnJvbSB0aGUgbWVhbgpvdXRsaWVycyA8LSBhbGxEYXRhW3doaWNoKGFicyhhbGxEYXRhJHN0RGV2RnJvbU1lYW4pID49IDMpLF0Kb3V0bGllcnMKYGBgCjxoND4gRGlzdHJpYnV0aW9uIDwvaDQ+CjxwPlRoZSByZXNwb25zZSB2YXJpYWJsZSBvZiBnb29kIGRheXMgd2FzIHRoZW4gYW5hbHl6ZWQgZm9yIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBhIGhpc3RvZ3JhbS48L3A+CmBgYHtyfQpoaXN0KGFsbERhdGEkZ29vZF9kYXlzKQpgYGAKPHA+QSBzcXVhcmVkIHRyYW5zZm9ybSB3YXMgdGhlbiBhcHBsaWVkIHRvIHRoZSBnb29kIGRheXMgdG8gbm9ybWFsaXplIHRoZSBnb29kIGRheXMgdmFyaWFibGUuPC9wPgpgYGB7cn0KIyBzY2F0dGVyIHBsb3Qgb2YgYWlyIHF1YWxpdHkgZ29vZCBkYXlzIGRhdGEKaGlzdCgoYWxsRGF0YSRnb29kX2RheXMpXjIpCgojIHRyYW5zZm9ybSB0aGUgYXZlcmFnZSBnb29kIGRheXMgYnkgc3F1YXJlIHJvb3QKYWxsRGF0YUdvb2REYXlzTm9ybWFsaXplZCA8LSBhbGxEYXRhWyxjKC0xLC0yLC0xMTotMTUpXQphbGxEYXRhR29vZERheXNOb3JtYWxpemVkJGdvb2RfZGF5cyA8LSAoKGFsbERhdGEkZ29vZF9kYXlzKV4yKQpgYGAKPGg0PiBBTk9WQSA8L2g0Pgo8cD5BTk9WQSdzIHdlcmUgZG9uZSB0byBzZWUgaWYgY2VydGFpbiBmZWF0dXJlcyBhbG9uZSB3ZXJlIGVub3VnaCB0byBwcmVkaWN0IHRoZSBwZXJjZW50YWdlIG9mIGdvb2QgZGF5cyBmb3IgYSBsb2NhdGlvbi4gVGhlc2Ugd2VyZSBkb25lIGZvciBtYWxlIHBlcmNlbnRhZ2UsIHdoaXRlIHBlcmNlbnRhZ2UsIGFuZCBwb3ZlcnR5IHBlcmNlbnRhZ2UuPC9wPgo8cD5UaGVuLCBBTk9WQSdzIHdlcmUgZG9uZSB0byBzZWUgaWYgdGhlc2UgZmVhdHVyZXMgd2l0aCBvdGhlciBmZWF0dXJlcyBwcmVzZW50IHdlcmUgYSBzaWduaWZpY2FudCBwcmVkaWN0b3IgdmFyaWFibGUuPC9wPgoKPGg1Pk1hbGUgUGVyY2VudGFnZSBPbmx5PC9oNT4KYGBge3J9CiMgYW5vdmEgdG8gc2VlIGlmIG1hbGUgcGVyY2VudGFnZSBhbG9uZSBpcyBlbm91Z2ggdG8gcHJlZGljdCBudW1iZXIgb2YgZ29vZCBkYXlzCmFvdi5nZW5kZXJPbmx5IDwtIGFvdihmb3JtdWxhID0gZ29vZF9kYXlzIH4gbWFsZV9wZXJjZW50LCBkYXRhID0gYWxsRGF0YUdvb2REYXlzTm9ybWFsaXplZCkKc3VtbWFyeShhb3YuZ2VuZGVyT25seSkKYGBgCjxwPiBCZWNhdXNlIHRoZSBwIHZhbHVlIGlzIGxlc3MgdGhhbiAwLjA1LCBtYWxlIHBlcmNlbnRhZ2UgYWxvbmUgaXMgZW5vdWdoIHRvIHByZWRpY3QgbnVtYmVyIG9mIGdvb2QgZGF5cy4gPC9wPgoKPGg1PldoaXRlIFBlcmNlbnRhZ2UgT25seTwvaDU+CmBgYHtyfQojIGFub3ZhIHRvIHNlZSBpZiB3aGl0ZSBwZXJjZW50YWdlIGFsb25lIGlzIGVub3VnaCB0byBwcmVkaWN0IG51bWJlciBvZiBnb29kIGRheXMKYW92LndoaXRlT25seSA8LSBhb3YoZm9ybXVsYSA9IGdvb2RfZGF5cyB+IHdoaXRlX3BlcmNlbnQsIGRhdGEgPSBhbGxEYXRhR29vZERheXNOb3JtYWxpemVkKQpzdW1tYXJ5KGFvdi53aGl0ZU9ubHkpCmBgYAo8cD4gQmVjYXVzZSB0aGUgcCB2YWx1ZSBpcyBsZXNzIHRoYW4gMC4wNSwgd2hpdGUgcGVyY2VudGFnZSBhbG9uZSBpcyBlbm91Z2ggdG8gcHJlZGljdCBudW1iZXIgb2YgZ29vZCBkYXlzLiA8L3A+Cgo8aDU+UG92ZXJ0eSBQZXJjZW50YWdlIE9ubHk8L2g1PgpgYGB7cn0KIyBhbm92YSB0byBzZWUgaWYgcG92ZXJ0eSBwZXJjZW50YWdlIGFsb25lIGlzIGVub3VnaCB0byBwcmVkaWN0IG51bWJlciBvZiBnb29kIGRheXMKYW92LnBvdmVydHlPbmx5IDwtIGFvdihmb3JtdWxhID0gZ29vZF9kYXlzIH4gcG92ZXJ0eV9wZXJjZW50LCBkYXRhID0gYWxsRGF0YUdvb2REYXlzTm9ybWFsaXplZCkKc3VtbWFyeShhb3YucG92ZXJ0eU9ubHkpCmBgYAo8cD4gQmVjYXVzZSB0aGUgcCB2YWx1ZSBpcyBncmVhdGVyIHRoYW4gMC4wNSwgcG92ZXJ0eSBwZXJjZW50YWdlIGFsb25lIGlzIG5vdCBlbm91Z2ggdG8gcHJlZGljdCBudW1iZXIgb2YgZ29vZCBkYXlzLiA8L3A+Cgo8aDU+QWxsIFBlcmNlbnRhZ2VzIHRvIFNlZSBpZiBQb3ZlcnR5LCBNYWxlLCBhbmQgV2hpdGUgUGVyY2VudGFnZXMgU2lnbmlmaWNhbnQgUHJlZGljdG9yczwvaDU+CmBgYHtyfQojIGFub3ZhIHRvIHNlZSBpZiBwb3ZlcnR5LCB3aGl0ZSwgYW5kIG1hbGUgcGVyY2VudGFnZXMgd2l0aCBvdGhlciBmZWF0dXJlcyBhcmUgc2lnbmlmaWNhbnQgcHJlZGljdG9ycwphb3YuZ29vZERheXNBbGwgPC0gYW92KGZvcm11bGEgPSBnb29kX2RheXMgfiAuLCBkYXRhID0gYWxsRGF0YUdvb2REYXlzTm9ybWFsaXplZCkKc3VtbWFyeShhb3YuZ29vZERheXNBbGwpCmBgYAo8cD4gVGhlIHAgdmFsdWVzIGZvciBtYWxlIGFuZCB3aGl0ZSBwZXJjZW50YWdlcyBhcmUgbGVzcyB0aGFuIDAuMDUsIG1ha2luZyB0aGVtIHNpZ25pZmljYW50IHByZWRpY3RvcnMuIFRoZSBwIHZhbHVlIGZvciBwb3ZlcnR5IHBlcmNlbnRhZ2UgaXMgZ3JlYXRlciB0aGFuIDAuMDUsIHRoZXJlZm9yZSBpdCBpcyBub3QgYSBzaWduaWZpY2FudCBwcmVkaWN0b3Igd2hlbiBhbGwgb2YgdGhlIG90aGVyIGZlYXR1cmVzIHByZXNlbnQgPC9wPgoKPGg0PiBTY2F0dGVyIFBsb3RzIDwvaDQ+CjxwPiBTY2F0dGVyIHBsb3RzIHdlcmUgYWxzbyBkb25lIHRvIHNlZSBpZiB0aGVyZSB3YXMgYSBsaW5lYXIgcmVncmVzc2lvbiByZWxhdGlvbnNoaXAgYmV0d2VlbiBnZW5kZXIsIHJhY2UsIGFuZCBwb3ZlcnR5IGZlYXR1cmVzIGFuZCBhaXIgcXVhbGl0eSBudW1iZXJzIDwvcD4KCjxoNT4gTWFsZSBQZXJjZW50YWdlIHZzIEdvb2QgRGF5cyA8L2g1PgpgYGB7cn0Kc2NhdHRlci5zbW9vdGgoYWxsRGF0YUdvb2REYXlzTm9ybWFsaXplZCRtYWxlX3BlcmNlbnQsIGFsbERhdGFHb29kRGF5c05vcm1hbGl6ZWQkZ29vZF9kYXlzKQpgYGAKPHA+IFRoZXJlIGRvZXMgbm90IHNlZW0gdG8gYmUgYSBzdHJvbmcgY29ycmVsYXRpb24gYmV0d2VlbiBnb29kIGRheXMgYW5kIHBlcmNlbnQgb2YgbWFsZXMuIEdvb2QgZGF5cyBzZWVtIHRvIGJlIGNvbnN0YW50IHdoZW4gY29tcGFyZWQgdG8gcGVyY2VudGFnZSBvZiBtYWxlcy48L3A+Cgo8aDU+IFdoaXRlIFBlcmNlbnRhZ2UgdnMgR29vZCBEYXlzIDwvaDU+CmBgYHtyfQpzY2F0dGVyLnNtb290aChhbGxEYXRhR29vZERheXNOb3JtYWxpemVkJHdoaXRlX3BlcmNlbnQsIGFsbERhdGFHb29kRGF5c05vcm1hbGl6ZWQkZ29vZF9kYXlzKQpgYGAKPHA+IEluIHRoaXMgcGxvdCwgdGhlIGRhdGEgc2VlbXMgc2NhdHRlcmVkIHdpdGhvdXQgYSBzaWduaWZpY2FudCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHdoaXRlIHBlcmNlbnRhZ2UgYW5kIGdvb2QgZGF5cy48L3A+Cgo8aDU+IE1hbGUgUGVyY2VudGFnZSB2cyBHb29kIERheXMgPC9oNT4KYGBge3J9CnNjYXR0ZXIuc21vb3RoKGFsbERhdGFHb29kRGF5c05vcm1hbGl6ZWQkcG92ZXJ0eV9wZXJjZW50LCBhbGxEYXRhR29vZERheXNOb3JtYWxpemVkJGdvb2RfZGF5cykKYGBgCjxwPiBUaGlzIHBsb3QgYWxzbyBzaG93cyBubyBzaWduaWZpY2FudCBjb3JyZWxhdGlvbiBhcyB0aGUgcG9pbnRzIGFyZSB2ZXJ5IHNjYXR0ZXJlZC4gPC9wPgoKPGg0PiBTcGVhcm1hbiBSYW5rIGFuZCBQZWFyc29uIE1vbWVudCBDb2VmZmljaWVudHMgPC9oND4KPHA+IFNwZWFybWFuIGFuZCBQZWFyc29uIGNvZWZmaWNpZW50cyB3ZXJlIGFsc28gdXNlZCB0byBhc3Nlc3MgY29ycmVsYXRpb24gYmV0d2VlbiBwb3ZlcnR5LCByYWNlLCBhbmQgZ2VuZGVyIHBlcmNlbnRhZ2VzIDwvcD4KCjxwPiBCZWZvcmUgdGhlIHRlc3RzIHdlcmUgcGVyZm9ybWVkIG9uIHRoZXNlIGZlYXR1cmVzLCBhbiBlZmZvcnQgd2FzIG1hZGUgdG8gbm9ybWFsaXplIHRoZW0gZm9yIGJldHRlciBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCB0ZXN0aW5nLiBObyB0cmFuc2Zvcm1zIHdlcmUgbWFkZSBvbiBhbnkgb2YgdGhlIGRhdGEuIFRoZSBwb3ZlcnR5IGRhdGEgd2FzIHJlbGF0aXZlbHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIFRoZSByYWNlIGFuZCBnZW5kZXIgZGF0YSB3YXMgc2tld2VkIGJ1dCBkaWQgbm90IHJlc3BvbmQgd2VsbCB0byB0cmFuc2Zvcm1zLiA8L3A+Cgo8aDU+IFBlYXJzb24gTW9tZW50IENvZWZmaWNpZW50OiBwZXJjZW50IG1hbGUgYW5kIG51bWJlciBvZiBnb29kIGFpciBxdWFsaXR5IGRheXM8L2g1PgpgYGB7cn0KY29yKGFsbERhdGFHb29kRGF5c05vcm1hbGl6ZWQkZ29vZF9kYXlzLCBhbGxEYXRhR29vZERheXNOb3JtYWxpemVkJG1hbGVfcGVyY2VudCwgbWV0aG9kID0gInBlYXJzb24iKQpgYGAKPGg1PiBTcGVhcm1hbiBSYW5rIENvZWZmaWNpZW50OiBwZXJjZW50IG1hbGUgYW5kIG51bWJlciBvZiBnb29kIGFpciBxdWFsaXR5IGRheXMgPC9oNT4KYGBge3J9CmNvcihhbGxEYXRhR29vZERheXNOb3JtYWxpemVkJGdvb2RfZGF5cywgYWxsRGF0YUdvb2REYXlzTm9ybWFsaXplZCRtYWxlX3BlcmNlbnQsIG1ldGhvZCA9ICJzcGVhcm1hbiIpCmBgYAo8aDU+IFBlYXJzb24gTW9tZW50IENvZWZmaWNpZW50OiBwZXJjZW50IHdoaXRlIGFuZCBudW1iZXIgb2YgZ29vZCBhaXIgcXVhbGl0eSBkYXlzIDwvaDU+CmBgYHtyfQpjb3IoYWxsRGF0YUdvb2REYXlzTm9ybWFsaXplZCRnb29kX2RheXMsIGFsbERhdGFHb29kRGF5c05vcm1hbGl6ZWQkd2hpdGVfcGVyY2VudCwgbWV0aG9kID0gInBlYXJzb24iKQpgYGAKPGg1PiBTcGVhcm1hbiBSYW5rIENvZWZmaWNpZW50OiBwZXJjZW50IHdoaXRlIGFuZCBudW1iZXIgb2YgZ29vZCBhaXIgcXVhbGl0eSBkYXlzIDwvaDU+CmBgYHtyfQpjb3IoYWxsRGF0YUdvb2REYXlzTm9ybWFsaXplZCRnb29kX2RheXMsIGFsbERhdGFHb29kRGF5c05vcm1hbGl6ZWQkd2hpdGVfcGVyY2VudCwgbWV0aG9kID0gInNwZWFybWFuIikKYGBgCjxoNT4gUGVhcnNvbiBNb21lbnQgQ29lZmZpY2llbnQ6IHBlcmNlbnQgcG92ZXJ0eSBhbmQgbnVtYmVyIG9mIGdvb2QgYWlyIHF1YWxpdHkgZGF5cyA8L2g1PgpgYGB7cn0KY29yKGFsbERhdGFHb29kRGF5c05vcm1hbGl6ZWQkZ29vZF9kYXlzLCBhbGxEYXRhR29vZERheXNOb3JtYWxpemVkJHBvdmVydHlfcGVyY2VudCwgbWV0aG9kID0gInBlYXJzb24iKQpgYGAKPGg1PiBTcGVhcm1hbiBSYW5rIENvZWZmaWNpZW50OiBwZXJjZW50IHBvdmVydHkgYW5kIG51bWJlciBvZiBnb29kIGFpciBxdWFsaXR5IGRheXM8L2g1CmBgYHtyfQpjb3IoYWxsRGF0YUdvb2REYXlzTm9ybWFsaXplZCRnb29kX2RheXMsIGFsbERhdGFHb29kRGF5c05vcm1hbGl6ZWQkcG92ZXJ0eV9wZXJjZW50LCBtZXRob2QgPSAic3BlYXJtYW4iKQpgYGAKPHA+IEZvciBhbGwgb2YgdGhlIGZlYXR1cmVzIGV2YWx1YXRlZCwgdGhlIFNwZWFybWFuIFJhbmsgYW5kIFBlYXJzb24gTW9tZW50IGNvZWZmaWNpZW50cyBhcmUgcmVsYXRpdmVseSBjbG9zZSB0byBlYWNoIG90aGVyLiBUaGlzIG1lYW5zIHRoYXQgb3V0bGllcnMgZG8gbm90IHNlZW0gdG8gYmUgaGF2aW5nIGFuIGVmZmVjdCBvbiB0aGUgZGF0YS4gSG93ZXZlciwgYWxsIG9mIHRoZSBjb2VmZmljaWVudHMgYXJlIHZlcnkgbG93IGFuZCBzaWduaWZpY2FudGx5IGJlbG93IDAuOC4gQmVjYXVzZSBub25lIG9mIHRoZSBjb2VmZmljaWVudHMgYXJlIDAsIHRoZXJlIHNlZW1zIHRvIGJlIHNvbWUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGVzZSBmZWF0dXJlcyBhbmQgdGhlIG51bWJlciBvZiBnb29kIGFpciBxdWFsaXR5IGRheXMsIGJ1dCBub3QgZW5vdWdoIHRvIHNheSB0aGF0IHRoZXJlIGlzIHNpZ25pZmljYW50IGNvcnJlbGF0aW9uLgpUaGVyZSBpcyB0aGUgbGVhc3QgY29ycmVsYXRpb24gYmV0d2VlbiBwZXJjZW50IHBvdmVydHkgYW5kIGdvb2QgYWlyIHF1YWxpdHkgYW5kIHRoZSBtb3N0IGNvcnJlbGF0aW9uIGJldHdlZW4gcGVyY2VudCBvZiBtYWxlcyBhbmQgZ29vZCBhaXIgcXVhbGl0eS4gTm9uZSBvZiB0aGVzZSBjb3JyZWxhdGlvbnMsIGhvd2V2ZXIsIGFyZSBzdHJvbmcuPC9wPgoKPHA+IFRoZSByYWNlIGFuZCBnZW5kZXIgZGF0YSBpcyBhbHNvIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZCB3aGljaCBtYXkgYmUgYWZmZWN0aW5nIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMuIEhvd2V2ZXIsIGJlY2F1c2UgdGhlIFNwZWFybWFuIFJhbmsgYW5kIFBlYXJzb24gTW9tZW50IGNvZWZmaWNpZW50cyBmb3IgdGhlc2UgMiBmZWF0dXJlcyB3ZXJlIHJlbGF0aXZlbHkgY2xvc2UsIHRoZSBkaXN0cmlidXRpb24gZG9lcyBub3Qgc2VlbSB0byBiZSBzaWduaWZpY2FudGx5IGFmZmVjdGluZyB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnRzIDwvcD4KCjxoMj4gQnVpbGRpbmcgdGhlIE1vZGVsKHMpIDwvaDI+CjxwPlRoZSBkZXNpcmVkIG1vZGVsIGZvciB0aGlzIGRhdGFzZXQgaXMgYSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB3aXRoIHNvbWUgcmVzcG9uc2UgdmFyaWFibGUgZGVhbGluZyB3aXRoIHRoZSBudW1iZXIgb2YgZGF5cyBvZiBhIGNlcnRhaW4gYWlyIHF1YWxpdHkuCgo8aDQ+IE1vZGVsIHdpdGggR29vZCBEYXlzIGFzIFJlc3BvbnNlIFZhcmlhYmxlIDwvaDQ+CjxwPiBUaGUgZmlyc3QgbW9kZWwgaGFzIHRoZSBnb29kIGRheXMgYXMgYSByZXNwb25zZSB2YXJpYWJsZS48L3A+CjxwPiBUaGUgZGF0YSB3YXMgZmlyc3QgcmFuZG9tbHkgc3BsaXQgdXAgNTAtNTAgaW50byBhIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uIHNldHMuPC9wPgpgYGB7cn0KIyB0cmFpbmluZyBkYXRhIHNldDogcmFuZG9tIGhhbGYgb2YgdGhlIGRhdGEKdHJhaW5pbmcgPC0gYWxsRGF0YUdvb2REYXlzTm9ybWFsaXplZFtzYW1wbGUobnJvdyhhbGxEYXRhR29vZERheXNOb3JtYWxpemVkKSwgbnJvdyhhbGxEYXRhR29vZERheXNOb3JtYWxpemVkKSAvIDIpLF0KCiMgdGVzdCBkYXRhIHNldDogdGhlIG90aGVyIGhhbGYgb2YgdGhlIGRhdGEKdGVzdCA8LSBhbGxEYXRhR29vZERheXNOb3JtYWxpemVkWy1jKGFzLm51bWVyaWMocm93bmFtZXModHJhaW5pbmcpKSksXQpgYGAKPHA+IFRvIGNyZWF0ZSBhIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLCBiYWNrd2FyZCBmaXR0aW5nIHdhcyB1c2VkIGFuZCBmZWF0dXJlcyB3aXRoIHAgdmFsdWVzID4gMC4wNSB3ZXJlIHJlbW92ZWQgYXQgZWFjaCBzdGVwLiA8L3A+CmBgYHtyfQojIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIHRyYWluaW5nIGRhdGEgYW5kIGJhY2t3YXJkIGZpdHRpbmcsIG9ubHkga2VlcGluZyBwIHZhbHVlcyA8IDAuMDUKZ29vZERheXNNb2RlbCA8LSBsbShmb3JtdWxhID0gZ29vZF9kYXlzIH4gLi0oaGlzcGFuaWNfcGVyY2VudCtwb3ZlcnR5X3BlcmNlbnQrYXNpYW5fcGVyY2VudCtibGFja19wZXJjZW50KSwgZGF0YSA9IHRyYWluaW5nKQpzdW1tYXJ5KGdvb2REYXlzTW9kZWwpCmBgYAo8cD4gTmV4dCwgdXNpbmcgdGhlIHRlc3QgZGF0YSBzZXQgd2FzIHVzZWQgdG8ganVkZ2UgdGhlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbCBieSBjYWxjdWxhdGluZyB0aGUgcHJlZGljdGlvbiBhY2N1cmFjeSB3aXRoIHRoZSBwcmVkaWN0ZWQgdmFsdWVzIGZvciB0aGUgdGVzdCBkYXRhIHNldC4gQUlDIGFuZCBCSUMgY2FsY3VsYXRpb25zIHdlcmUgYWxzbyBwZXJmb3JtZWQgYXMgdGhleSBhcmUgZ29vZCBtZWFzdXJlcyBmb3IgbW9kZWwgc2VsZWN0aW9uLCBpbiBjYXNlIG90aGVyIG1vZGVscyBhcmUgY3JlYXRlZCBmb3IgdGhpcyBkYXRhLiA8L3A+CmBgYHtyfQojIHJldmVyc2UgdGhlIHRyYW5zZm9ybSBvZiB0aGUgZGF0YSBhbmQgbWFrZSBwcmVkaWN0aW9ucyBiYXNlZCBvbiB0aGF0IGRhdGEKdGVzdFVubm9ybWFsaXplZCA8LSB0ZXN0CnRlc3RVbm5vcm1hbGl6ZWQkZ29vZF9kYXlzIDwtIGFsbERhdGFbLWMoYXMubnVtZXJpYyhyb3duYW1lcyh0cmFpbmluZykpKSxjKCJnb29kX2RheXMiKV0KZ29vZERheXNQcmVkcyA8LSBwcmVkaWN0KGdvb2REYXlzTW9kZWwsIHRlc3RVbm5vcm1hbGl6ZWQpCgojIGRhdGEgZnJhbWUgb2YgYWN0dWFsIHZhbHVlcyB2cyBwcmVkaWN0ZWQgdmFsdWVzCmFjdHVhbHNfcHJlZHNfZ29vZCA8LSBjYmluZChkYXRhLmZyYW1lKGFjdHVhbHMgPSB0ZXN0VW5ub3JtYWxpemVkJGdvb2RfZGF5cywgcHJlZGljdGVkcyA9IGdvb2REYXlzUHJlZHMpKQoKIyBwcmVkaWN0aW9uIGFjY3VyYWN5CmNvcnJlbGF0aW9uX2FjY3VyYWN5IDwtIGNvcihhY3R1YWxzX3ByZWRzX2dvb2QpCnBhc3RlKCJQcmVkaWN0aW9uIEFjY3VyYWN5OiAiLCBjb3JyZWxhdGlvbl9hY2N1cmFjeVsxLDJdICogMTAwLCAiJSIsIHNlcD0iIikKCiMgZml0IEFJQyBhbmQgQklDIGNhbGN1bGF0aW9ucwpwYXN0ZSgiQUlDOiIsIEFJQyhnb29kRGF5c01vZGVsKSkKcGFzdGUoIkJJQzoiLCBCSUMoZ29vZERheXNNb2RlbCkpCmBgYApVc2luZyB0aGUgdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSBzZXQgdG8gY2FsY3VsYXRlIHRoZSBwcmVkaWN0aW9uIGFjY3VyYWN5LCB0aGUgbW9kZWwgd2FzIGNvcnJlY3QgYWJvdXQgMzklIG9mIHRoZSB0aW1lLgoKPGg0PiBNb2RlbCB3aXRoIENvbWJvIG9mIEdvb2QgYW5kIE1vZGVyYXRlIERheXMgYXMgUmVzcG9uc2UgVmFyaWFibGUgPC9oND4KPHA+IFRvIGNyZWF0ZSB0aGlzIG1vZGVsLCB0aGUgc3VtIG9mIGdvb2QgYW5kIG1vZGVyYXRlIGRheXMgd2FzIHRha2VuIHRvIGNyZWF0ZSB0aGUgZmVhdHVyZSBvZiBhY2NlcHRhYmxlIGFpciBxdWFsaXR5IGRheXMuIDwvcD4KYGBge3J9CmFsbERhdGFTdW1Hb29kTW9kZXJhdGUgPC0gYWxsRGF0YVssLTE1XQoKIyBjcmVhdGUgYWNjZXB0YWJsZSBkYXlzIGZlYXR1cmUKYWxsRGF0YVN1bUdvb2RNb2RlcmF0ZSRhY2NlcHRhYmxlX2RheXMgPC0gYWxsRGF0YSRnb29kX2RheXMgKyBhbGxEYXRhJG1vZGVyYXRlX2RheXMKCiMgb25seSBsZWF2ZSBhY2NlcHRhYmxlIGRheXMgZmVhdHVyZSBpbiBhbmQgb21pdCBvdXQgdGhlIHJlc3Qgb2YgdGhlIGFpciBxdWFsaXR5IGFuZCBzdGF0ZSBhbmQgY291bnR5IGZlYXR1cmVzCmFsbERhdGFTdW1Hb29kTW9kZXJhdGUgPC0gYWxsRGF0YVN1bUdvb2RNb2RlcmF0ZVssYygtMSwtMiwtMTA6LTE0KV0KYGBgCjxwPiBUaGVuLCBvdXRsaWVycyB3ZXJlIGlkZW50aWZpZWQgYWdhaW4gaW4gdGhlIHNhbWUgbWV0aG9kIG9mIHN0YW5kYXJkIGRldmlhdGlvbiBmcm9tIHRoZSBtZWFuIGFzIHRoZSBsYXN0IG1vZGVsLiBPbmNlIGFnYWluLCBJIGRlY2lkZWQgdG8gbGVhdmUgdGhlc2Ugb3V0bGllcnMgaW4gZm9yIG1vcmUgYWNjdXJhdGUgZGF0YSBhbmQgYmVjYXVzZSBJIGJlbGlldmUgdGhpcyBkYXRhIGlzIHNpZ25pZmljYW50IGZvciBhbmFseXNpcyA8L3A+CmBgYHtyfQphY2NlcHRhYmxlRGF5c01lYW4gPC0gbWVhbihhbGxEYXRhU3VtR29vZE1vZGVyYXRlJGFjY2VwdGFibGVfZGF5cykgIyBhY2NlcHRhYmxlIGRheXMgbWVhbgphY2NlcHRhYmxlRGF5c1N0RGV2IDwtIHNkKGFsbERhdGFTdW1Hb29kTW9kZXJhdGUkYWNjZXB0YWJsZV9kYXlzKSAjIGFjY2VwdGFibGUgZGF5cyBzdCBkZXYKCiMgZmluZCBhbnkgdmFsdWVzIGluIGdvb2QgZGF5cyB0aGF0IGFyZSAzIG9yIG1vcmUgc3RhbmRhcmQgZGV2aWF0aW9ucyBmcm9tIHRoZSBtZWFuCmFsbERhdGFTdW1Hb29kTW9kZXJhdGUkc3REZXZGcm9tTWVhbiA8LSAoKGFsbERhdGFTdW1Hb29kTW9kZXJhdGUkYWNjZXB0YWJsZV9kYXlzIC0gYWNjZXB0YWJsZURheXNNZWFuKSAvIGFjY2VwdGFibGVEYXlzU3REZXYpCgphbGxEYXRhU3VtR29vZE1vZGVyYXRlW3doaWNoKGFicyhhbGxEYXRhU3VtR29vZE1vZGVyYXRlJHN0RGV2RnJvbU1lYW4pID49IDMpLF0KCiMgb21pdCB0aGUgc3RhbmRhcmQgZGV2IGZyb20gbWVhbiBjb2x1bW5zIGZvciBtb2RlbCBjcmVhdGlvbgphbGxEYXRhU3VtR29vZE1vZGVyYXRlIDwtIGFsbERhdGFTdW1Hb29kTW9kZXJhdGVbLC05XQpgYGAKPHA+IEEgaGlzdG9ncmFtIHdhcyB0aGVuIHVzZWQgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlLjwvcD4KPHA+IEJlY2F1c2Ugbm9uZSBvZiB0aGUgdHJhbnNmb3JtIGhhZCBhIHNpZ25pZmljYW50IGVmZmVjdCBvbiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBoZWF2aWx5IHJpZ2h0IHNrZXdlZCBkYXRhLCB0aGUgZGF0YSB3YXMgbGVmdCBhcyBpcy4gPC9wPgpgYGB7cn0KaGlzdChhbGxEYXRhU3VtR29vZE1vZGVyYXRlJGFjY2VwdGFibGVfZGF5cykKYGBgCjxwPiBUaGUgZGF0YSB3YXMgdGhlbiBvbmNlIGFnYWluIHJhbmRvbWx5IHNwbGl0IHVwIDUwLTUwIGludG8gdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSBzZXRzLiA8L3A+CgpgYGB7cn0KIyB0cmFpbmluZyBkYXRhIHNldDogcmFuZG9tIGhhbGYgb2YgdGhlIGRhdGEKdHJhaW5pbmdBY2NlcHRhYmxlRGF5cyA8LSBhbGxEYXRhU3VtR29vZE1vZGVyYXRlW3NhbXBsZShucm93KGFsbERhdGFTdW1Hb29kTW9kZXJhdGUpLCBucm93KGFsbERhdGFTdW1Hb29kTW9kZXJhdGUpIC8gMiksXQoKIyB0ZXN0IGRhdGEgc2V0OiB0aGUgb3RoZXIgaGFsZiBvZiB0aGUgZGF0YQp0ZXN0QWNjZXB0YWJsZURheXMgPC0gYWxsRGF0YVN1bUdvb2RNb2RlcmF0ZVstYyhhcy5udW1lcmljKHJvd25hbWVzKHRyYWluaW5nKSkpLF0KYGBgCjxwPiBUaGVuLCB0byBjcmVhdGUgYSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCwgYmFja3dhcmQgZml0dGluZyB3YXMgYWdhaW4gdXNlZCBhbmQgZmVhdHVyZXMgd2l0aCBwIHZhbHVlcyA+IDAuMDUgd2VyZSByZW1vdmVkIGF0IGVhY2ggc3RlcC4gPC9wPgpgYGB7cn0KIyBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB1c2luZyB0cmFpbmluZyBkYXRhIGFuZCBiYWNrd2FyZCBmaXR0aW5nLCBvbmx5IGtlZXBpbmcgcCB2YWx1ZXMgPCAwLjA1CmFjY2VwdGFibGVEYXlzTW9kZWwgPC0gbG0oZm9ybXVsYSA9IGFjY2VwdGFibGVfZGF5cyB+IC4tKHBvdmVydHlfcGVyY2VudCttYWxlX3BlcmNlbnQpLCBkYXRhID0gdHJhaW5pbmdBY2NlcHRhYmxlRGF5cykKc3VtbWFyeShhY2NlcHRhYmxlRGF5c01vZGVsKQpgYGAKPHA+IE5leHQsIHVzaW5nIHRoZSB0ZXN0IGRhdGEgc2V0IHdhcyB1c2VkIHRvIGp1ZGdlIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwgYnkgY2FsY3VsYXRpbmcgdGhlIHByZWRpY3Rpb24gYWNjdXJhY3kgd2l0aCB0aGUgcHJlZGljdGVkIHZhbHVlcyBmb3IgdGhlIHRlc3QgZGF0YSBzZXQuIEFJQyBhbmQgQklDIGNhbGN1bGF0aW9ucyB3ZXJlIGFsc28gcGVyZm9ybWVkIGFzIHRoZXkgYXJlIGdvb2QgbWVhc3VyZXMgZm9yIG1vZGVsIHNlbGVjdGlvbiwgaW4gY2FzZSBvdGhlciBtb2RlbHMgYXJlIGNyZWF0ZWQgZm9yIHRoaXMgZGF0YS4gPC9wPgpgYGB7cn0KYWNjZXB0YWJsZURheXNQcmVkcyA8LSBwcmVkaWN0KGFjY2VwdGFibGVEYXlzTW9kZWwsIHRlc3RBY2NlcHRhYmxlRGF5cykKCiMgZGF0YSBmcmFtZSBvZiBhY3R1YWwgdmFsdWVzIHZzIHByZWRpY3RlZCB2YWx1ZXMKYWN0dWFsc19wcmVkc19hY2NlcHRhYmxlIDwtIGNiaW5kKGRhdGEuZnJhbWUoYWN0dWFscyA9IHRlc3RBY2NlcHRhYmxlRGF5cyRhY2NlcHRhYmxlX2RheXMsIHByZWRpY3RlZHMgPSBhY2NlcHRhYmxlRGF5c1ByZWRzKSkKCiMgcHJlZGljdGlvbiBhY2N1cmFjeQpjb3JyZWxhdGlvbl9hY2N1cmFjeSA8LSBjb3IoYWN0dWFsc19wcmVkc19hY2NlcHRhYmxlKQpwYXN0ZSgiUHJlZGljdGlvbiBBY2N1cmFjeTogIiwgY29ycmVsYXRpb25fYWNjdXJhY3lbMSwyXSAqIDEwMCwgIiUiLCBzZXA9IiIpCgojIGZpdCBBSUMgYW5kIEJJQyBjYWxjdWxhdGlvbnMKcGFzdGUoIkFJQzoiLCBBSUMoYWNjZXB0YWJsZURheXNNb2RlbCkpCnBhc3RlKCJCSUM6IiwgQklDKGFjY2VwdGFibGVEYXlzTW9kZWwpKQpgYGAKVXNpbmcgdGhlIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGEgc2V0IHRvIGNhbGN1bGF0ZSB0aGUgcHJlZGljdGlvbiBhY2N1cmFjeSwgdGhlIG1vZGVsIHdhcyBjb3JyZWN0IGFib3V0IDI0JSBvZiB0aGUgdGltZS4KCjxoMj4gRXZhbHVsYXRpb24gb2YgTW9kZWxzIDwvaDI+Cgo8aDQ+IE1vZGVsIDE6IEdvb2QgRGF5cyBhcyBSZXNwb25zZSBWYXJpYWJsZSA8L2g0Pgo8cD4gT3ZlcmFsbCwgdGhpcyBtb2RlbCBpcyBub3QgYSB2ZXJ5IGdvb2QgbWVhc3VyZSBmb3IgcHJlZGljdGluZyB0aGUgcGVyY2VudGFnZSBvZiBnb29kIGFpciBxdWFsaXR5IGRheXMgYSBsb2NhdGlvbiB3aWxsIGhhdmUuIEJvdGggdGhlIG11bHRpcGxlIFIgc3F1YXJlZCBhbmQgdGhlIGFkanVzdGVkIFIgc3F1YXJlZCBhcmUgdmVyeSBsb3cgYW5kIGFyZSBzaWduaWZpY2FudGx5IGJlbG93IDAuNy4gVGhpcyBtZWFucyB0aGF0IHRoZSB2YXJpYXRpb24gb2YgZ29vZCBhaXIgcXVhbGl0eSBkYXlzIGlzIG5vdCBzaWduaWZpY2FudGx5IGV4cGxhaW5lZCBieSB0aGlzIG1vZGVsLiBUaGUgY2FsY3VsYXRlZCBNQUQgZm9yIHRoaXMgbW9kZWwgaXMgMjguNjYgd2hpY2ggaXMgbm90IHZlcnkgY2xvc2UgdG8gMCwgbWVhbmluZyB0aGVyZSBpcyBhIHNpZ25pZmljYW50IGFtb3VudCBvZiBkZXZpYXRpb24gaW4gdGhpcyBtb2RlbC4gVGhlIG1vZGVsIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgYmVjYXVzZSB0aGUgcC12YWx1ZXMgZm9yIGVhY2ggZmVhdHVyZSBhcyB3ZWxsIGFzIHRoZSBvdmVyYWxsIG1vZGVsIHAtdmFsdWUgYXJlIHdlbGwgYmVsb3cgMC4wNS4KCjxoND4gTW9kZWwgMjogR29vZCtNb2RlcmF0ZSBEYXlzIGFzIFJlc3BvbnNlIFZhcmlhYmxlIDwvaDQ+CjxwPiBUaGlzIG1vZGVsIGlzIGFsc28gbm90IGEgZ3JlYXQgbWVhc3VyZSBmb3IgcHJlZGljdGluZyB0aGUgcGVyY2VudGFnZSBvZiBhY2NlcHRhYmxlIGRheXMgKHRoYXQgaXMsIGdvb2QgYW5kIG1vZGVyYXRlIGRheXMpLiBUaGUgbXVsdGlwbGUgUiBzcXVhcmVkIGFuZCB0aGUgYWRqdXN0ZWQgUiBzcWF1cmVzIGFyZSBldmVuIGxvd2VyIGluIHRoaXMgbW9kZWwgdGhhbiB0aGUgcHJldmlvdXMgbW9kZWwuIFRoaXMgbWVhbnMgdGhhdCBldmVuIGxlc3MgdmFyaWF0aW9uIGluIHRoZSBhY2NlcHRhYmxlIGFpciBxdWFsaXR5IGRheXMgaXMgZGVzY3JpYmVkIGJ5IHRoaXMgbW9kZWwgdGhhbiB0aGUgcHJldmlvdXMgbW9kZWwuIFRoZSBjYWxjdWxhdGVkIE1BRCBmb3IgdGhpcyBtb2RlbCwgaG93ZXZlciwgaXMgMC4wNzQsIHdoaWNoIGlzIG11Y2ggY2xvc2VyIHRvIDAgdGhhbiB0aGUgcHJldmlvdXMgbW9kZWwuIE1lYW5pbmcgdGhhdCB0aGVyZSBpcyBtdWNoIGxlc3MgZGV2aWF0aW9uIGluIHRoaXMgbW9kZWwgdGhhbiB0aGUgcHJldmlvdXMgbW9kZWwuIFRoaXMgbWFrZXMgc2Vuc2UgYmVjYXVzZSBvdXRzaWRlIG9mIHRoZSAxNSBvdXRsaWVycywgdGhlIHBlcmNlbnRhZ2Ugb2YgYWNjZXB0YWJsZSBkYXlzIHdhcyBtb3N0bHkgYXJvdW5kIDkwJS4gVGhpcyBtb2RlbCBpcyBhbHNvIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgd2l0aCBvdmVyYWxsIGFuZCBpbmRpdmlkdWFsIGZlYXR1cmUgcC12YWx1ZXMgd2VsbCBiZWxvdyAwLjA1LiBUaGUgQUlDIGFuZCBCSUMgb2YgdGhpcyBtb2RlbCBhcmUgYWxzbyBtdWNoIGxvd2VyIHRoYW4gdGhlIHByZXZpb3VzIG1vZGVsLCBtZWFuaW5nIHRoaXMgbW9kZWwgaXMgbW9yZSBsaWtlbHkgdG8gYmUgdGhlIHRydWUgbW9kZWwgYW5kIGlzIGNsb3NlciB0byB0aGUgdHJ1dGguPC9wPgoKPGgyPiBDb25jbHVzaW9uIDwvaDI+CjxwPiBPdmVyYWxsLCBJIGh5cG90aGVzaXplZCB0aGF0IGJ1aWxkaW5nIGEgbW9kZWwgd2l0aCB0aGVzZSBhYm92ZSBkZW1vZ3JhcGhpYyBmZWF0dXJlcyB3b3VsZCBiZSBhIGdvb2QgcHJlZGljdG9yIGZvciBhaXIgcXVhbGl0eSBpcyBVUyBsb2NhdGlvbnMuIEhvd2V2ZXIsIGZyb20gdGhlIHR3byBtb2RlbHMgSSBoYXZlIGJ1aWx0IGFuZCB0aGUgZGF0YSBJIGFnZ3JlZ2F0ZWQsIHRoaXMgZG9lcyBub3Qgc2VlbSB0byBiZSB0aGUgY2FzZS4gPC9wPgo8cD4gVGhlcmUgYXJlIGEgZmV3IHJlYXNvbnMgd2h5IHRoaXMgd2FzIHRoZSByZXN1bHQ6IDwvcD4KPHVsPgogIDxsaT4gPHA+IFRoZXJlIGlzIG5vdCBlbm91Z2ggYWlyIHF1YWxpdHkgZGF0YS4gV2hpbGUgdGhlcmUgd2VyZSBhcm91bmQgMywwMDAgcmVjb3JkcyBmcm9tIHRoZSBjZW5zdXMgZGVtb2dyYXBoaWMgZGF0YSwgdGhlcmUgd2VyZSBvbmx5IGFib3V0IDEsMDAwIHJlY29yZHMgZm9yIHRoZSBhaXIgcXVhbGl0eSBkYXRhLiBUaGlzIG1lYW5zIHRoYXQgYXJlYXMgdGhhdCBjb3VsZCBoYXZlIGNvbnRyaWJ1dGVkIHRvIGEgbW9yZSBzdWNjZXNzZnVsIG1vZGVsIGRpZCBub3QgaGF2ZSBhaXIgcXVhbGl0eSByZWNvcmRlZC4gPC9wPiA8L2xpPgogIDxsaT4gPHA+VGhlIGNvcnJlbGF0aW9uIGFjdHVhbGx5IGRvZXMgbm90IGV4aXN0LiBJIGRvIG5vdCBiZWxpZXZlIHRoaXMgaXMgdGhlIGNhc2UsIGJlY2F1c2UgaXQgaGFzIGJlZW4gc2hvd24gdGhhdCB0aGUgY29ycmVsYXRpb24gZXhpc3RzIGZvciB3YXRlciBxdWFsaXR5IGFuZCB0aGUgbWFueSBlbnZpcm9ubWVudGFsIHJhY2lzbSBjYXNlcyBpbiB0aGUgVVMuPC9wPjwvbGk+CiAgPGxpPiA8cD5UaGUgMjAxMCBkYXRhIGlzIHRvbyBvbGQgYW5kIGluIDIwMTAsIGFpciBxdWFsaXR5IHdhcyBub3QgZGVwZW5kZW50IG9uIGFueSBkZW1vZ3JhcGhpY3MuIEkgZG9uJ3QsIGhvd2V2ZXIsIHRoaW5rIHRoaXMgd2FzIHRoZSBjYXNlIGFzIDIwMTAgd2FzIG9ubHkgNyB5ZWFycyBhZ28uIDwvcD48L2xpPgo8L3VsPg==